package me.zhengjie.tcp;


import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.requests.WriteSingleRegisterRequest;
import com.digitalpetri.modbus.responses.ModbusResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import com.digitalpetri.modbus.responses.WriteSingleRegisterResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Component
@RequiredArgsConstructor
public class CircleLibraryTCP {

    private final CircleLibraryConfig circleLibraryConfigA;

    static ModbusTcpMaster master;

    private final ConcurrentHashMap<Integer, ReentrantLock> addressLocks = new ConcurrentHashMap<>();

    // 获取锁的方法
    private ReentrantLock getLockForAddress(int address) {
        return addressLocks.computeIfAbsent(address, key -> new ReentrantLock(true)); // 公平锁
    }


    /**
     * 获取TCP协议的Master
     */
    @PostConstruct
    public void initModbusTcpMaster() {
        try {
            if (master == null) {
                // 创建配置
                ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder(circleLibraryConfigA.getIp()).setPort(circleLibraryConfigA.getPort()).build();
                master = new ModbusTcpMaster(config);
                master.connect();
            }
        } catch (Exception e) {
            log.error("叠合区plc连接失败，失败原因：{}", e.toString());
        }
    }

    public Integer readHoldingRegisters(int address,int unitId) {
        try {
            CompletableFuture<ReadHoldingRegistersResponse> future = master
                    .sendRequest(new ReadHoldingRegistersRequest(address, 1), unitId);
            ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();
            log.info("打印返回结果：{}",readHoldingRegistersResponse.toString());
            if (readHoldingRegistersResponse != null) {
                ByteBuf buf = readHoldingRegistersResponse.getRegisters();
                int i = buf.readUnsignedShort();
                ReferenceCountUtil.release(readHoldingRegistersResponse);
                return i;
            }
            return null;
        } catch (InterruptedException | ExecutionException e) {
            log.error("读取物流区寄存器地址值时出错，错误原因：{}", e.toString());
            return null;
        }

    }

    public String readHoldingRegistersString(int address,int unitId) {
        try {
            CompletableFuture<ReadHoldingRegistersResponse> future = master
                    .sendRequest(new ReadHoldingRegistersRequest(address, 1), unitId);
            ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();
            if (readHoldingRegistersResponse != null) {
                ByteBuf buf = readHoldingRegistersResponse.getRegisters();
                byte[] bytes = new byte[2];
                buf.readBytes(bytes);
                ReferenceCountUtil.release(readHoldingRegistersResponse);
                return new String(bytes, StandardCharsets.UTF_8);
            }
            return null;
        } catch (InterruptedException | ExecutionException e) {
            log.error(e.getMessage());
            return null;
        }

    }

    /*public void write(int address, int value){
        // 发送单个寄存器数据，一般是无符号16位值：比如10
        CompletableFuture<ModbusResponse> future = master.sendRequest(new WriteSingleRegisterRequest(address, value), 0);
    }*/

    private boolean writeToRegister(int address, int value,int unitId) throws Exception {
        try {
            // 发送异步写入请求
            CompletableFuture<ModbusResponse> future = master.sendRequest(new WriteSingleRegisterRequest(address, value), unitId);

            // 阻塞等待结果返回，设置超时时间
            ModbusResponse response = future.get(100, TimeUnit.MILLISECONDS);

            // 检查响应是否为写入成功的类型
            if (response instanceof WriteSingleRegisterResponse) {
                WriteSingleRegisterResponse writeResponse = (WriteSingleRegisterResponse) response;

                // 校验返回的地址和值是否匹配
                if (writeResponse.getAddress() == address && writeResponse.getValue() == value) {
                    return true; // 写入成功
                } else {
                    log.warn("写入响应内容不匹配：地址={}, 值={}, 响应={}", address, value, writeResponse);
                }
            } else {
                log.warn("响应类型错误，预期=WriteSingleRegisterResponse，实际={}", response.getClass().getSimpleName());
            }

            return false; // 写入失败
        } catch (TimeoutException e) {
            log.error("写入超时，地址={}，值={}", address, value, e);
            return false; // 写入超时
        } catch (Exception e) {
            log.error("写入异常，地址={}，值={}", address, value, e);
            throw e; // 抛出异常以便外层重试逻辑处理
        }
    }

    public boolean writeWithRetry(int address, int value, int unitId, int maxRetries) {
        int attempts = 0;
        ReentrantLock lock = getLockForAddress(address); // 获取地址锁
        boolean acquired = false;

        while (attempts < maxRetries) {
            try {
                acquired = lock.tryLock(500, TimeUnit.MILLISECONDS); // 尝试获取锁
                if (acquired) {
                    // 尝试发送写入请求并等待结果
                    boolean success = writeToRegister(address, value,unitId);
                    if (success) {
                        log.info("写入成功：地址={}，值={}", address, value);
                        return true; // 写入成功，直接退出
                    } else {
                        log.warn("写入失败，地址={}，值={}", address, value);
                        return false;
                    }
                } else {
                    log.warn("获取锁超时，第{}次重试，地址={}", attempts + 1, address);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 恢复中断状态
                log.error("线程中断，地址={}，值={}", address, value, e);
                break; // 中断异常退出
            } catch (Exception e) {
                log.error("写入异常，第{}次重试，地址={}，值={}", attempts + 1, address, value, e);
            } finally {
                if (acquired) {
                    lock.unlock(); // 确保锁释放
                }
            }
            attempts++; // 增加重试计数
        }
        log.error("写入失败，超出最大重试次数，地址={}，值={}", address, value);
        return false;
    }

}
